import json
import logging
import tempfile
import os
import requests
import numpy as np
from urllib.parse import parse_qs

from channels.generic.websocket import AsyncWebsocketConsumer
from channels.db import database_sync_to_async
from asgiref.sync import sync_to_async

from django.conf import settings
from django.contrib.auth.models import User, AnonymousUser
from rest_framework.authtoken.models import Token
from django.core.exceptions import ObjectDoesNotExist
from django.utils import timezone

from .models import Device, AccessLog
from face_db.models import FaceData
from face_db.services import verify_face

logger = logging.getLogger(__name__)

class DeviceConsumer(AsyncWebsocketConsumer):
    """
    WebSocket consumer for device connections with authentication.
    Supports both user authentication (via token) and device authentication (via device key).
    """

    async def connect(self):
        """
        Handle WebSocket connection with authentication.
        """
        self.device_id = self.scope['url_route']['kwargs']['device_id']
        self.device_group_name = f'device_{self.device_id}'
        self.user = None
        self.is_device = False

        # Authenticate the connection
        auth_result = await self.authenticate()

        if not auth_result:
            logger.warning(f"Unauthorized connection attempt to device {self.device_id}")
            await self.close(code=4001)  # Custom close code for authentication failure
            return

        # Join device group
        await self.channel_layer.group_add(
            self.device_group_name,
            self.channel_name
        )

        await self.accept()

        # Send connection confirmation
        await self.send(text_data=json.dumps({
            'event': 'connection_established', # 统一使用 'event' 协议
            'device_id': self.device_id,
            'user_type': 'device' if self.is_device else 'user',
            'user_id': self.user.id if self.user and not isinstance(self.user, AnonymousUser) else None,
            'message': 'Connected successfully'
        }))

        logger.info(f"Device {self.device_id} connected successfully. User: {self.user}, Is Device: {self.is_device}")

        # 更新设备状态并通知管理员
        await self.update_and_broadcast_device_status('online')

    async def disconnect(self, close_code):
        """
        Handle WebSocket disconnection.
        """
        # 更新设备状态并通知管理员
        await self.update_and_broadcast_device_status('offline')

        # Leave device group
        await self.channel_layer.group_discard(
            self.device_group_name,
            self.channel_name
        )

        logger.info(f"Device {self.device_id} disconnected. Close code: {close_code}")

    async def receive(self, text_data):
        """
        Handle incoming WebSocket messages.
        """
        try:
            text_data_json = json.loads(text_data)
            event = text_data_json.get('event')
            # For device communication, the payload is the entire JSON object itself.
            data = text_data_json

            logger.info(f"Received event '{event}' from {self.device_id} with data: {data}")

            # Route events based on type
            if event == 'nfc_scan':
                await self.handle_nfc_scan(data)
            elif event == 'face_detection':
                await self.handle_face_detection(data)
            elif event == 'door_status':
                await self.handle_door_status(data)
            elif event == 'heartbeat':
                await self.handle_heartbeat(data)
            elif event == 'device_register':
                logger.info(f"Device {self.device_id} registration event acknowledged.")
                # 发送一个ack来确认，防止连接被意外关闭
                await self.send(text_data=json.dumps({
                    'event': 'device_register_ack',
                    'status': 'success'
                }))
            else:
                logger.warning(f"Unknown event type: {event}")
                await self.send_error(f"Unknown event type: {event}")

        except json.JSONDecodeError as e:
            logger.error(f"Invalid JSON received from {self.device_id}: {e}")
            await self.send_error("Invalid JSON format")
        except Exception as e:
            logger.error(f"Error processing message from {self.device_id}: {e}")
            await self.send_error("Internal server error")

    async def handle_nfc_scan(self, data):
        """
        Handle NFC card scan events with two-factor face verification.
        """
        card_id = data.get('card_id')
        if not card_id:
            await self.send_error("Missing card_id in NFC scan data")
            return

        logger.info(f"NFC Card {card_id} scanned on device {self.device_id}. Initiating 2FA.")

        user = await self.get_user_by_nfc_card(card_id)
        device = await self.get_device()

        if not user:
            logger.warning(f"Unknown NFC card: {card_id} on device {self.device_id}")
            await self.send(text_data=json.dumps({'event': 'access_denied', 'reason': 'unknown_card'}))
            await self.log_and_broadcast_access(user=None, device=device, is_success=False, failure_reason="unknown_card")
            return

        # --- Face Verification Step ---
        try:
            # 1. Get stored face embedding
            stored_embedding = await self.get_face_embedding_for_user(user)
            if stored_embedding is None:
                logger.warning(f"User {user.username} has no registered face data.")
                await self.send(text_data=json.dumps({'event': 'access_denied', 'reason': 'no_face_registered'}))
                await self.log_and_broadcast_access(user=user, device=device, is_success=False, failure_reason="no_face_registered")
                return

            # 2. Get camera service URL for this device
            camera_service_name = device.camera_service_name
            if not camera_service_name:
                logger.error(f"Device {device.device_id} has no associated camera service.")
                await self.send(text_data=json.dumps({'event': 'access_denied', 'reason': 'no_camera_configured'}))
                await self.log_and_broadcast_access(user=user, device=device, is_success=False, failure_reason="no_camera_configured")
                return

            camera_service_url = settings.CAMERA_SERVICES.get(camera_service_name)
            if not camera_service_url:
                logger.error(f"Camera service '{camera_service_name}' is not configured in settings.")
                await self.send(text_data=json.dumps({'event': 'access_denied', 'reason': 'camera_service_not_configured'}))
                await self.log_and_broadcast_access(user=user, device=device, is_success=False, failure_reason="camera_service_not_configured")
                return

            # 3. Capture live image from camera service
            capture_url = f"{camera_service_url.rstrip('/')}/capture"
            logger.info(f"Requesting image from camera service: {capture_url}")

            response = await sync_to_async(requests.get)(capture_url, timeout=10)
            response.raise_for_status()
            live_image_bytes = response.content

            # 4. Save snapshot to permanent location
            import uuid
            from datetime import datetime
            snapshot_filename = f"access_{device.device_id}_{user.username}_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{uuid.uuid4().hex[:8]}.jpg"
            snapshot_dir = os.path.join(settings.MEDIA_ROOT, 'access_snapshots')
            os.makedirs(snapshot_dir, exist_ok=True)
            snapshot_path = os.path.join(snapshot_dir, snapshot_filename)

            # Save the captured image
            with open(snapshot_path, 'wb') as f:
                f.write(live_image_bytes)

            # 5. Verify face against stored embedding
            is_match = await sync_to_async(verify_face)(
                image_path=snapshot_path,
                stored_embedding=stored_embedding
            )

            # 6. Handle verification result
            if is_match:
                logger.info(f"Face verification successful for user {user.username}.")
                await self.send(text_data=json.dumps({'event': 'access_granted', 'user': user.username}))
                await self.log_and_broadcast_access(user=user, device=device, is_success=True, snapshot_path=f"access_snapshots/{snapshot_filename}")
            else:
                logger.warning(f"Face verification failed for user {user.username}.")
                await self.send(text_data=json.dumps({'event': 'access_denied', 'reason': 'face_mismatch'}))
                await self.log_and_broadcast_access(user=user, device=device, is_success=False, failure_reason="face_mismatch", snapshot_path=f"access_snapshots/{snapshot_filename}")

        except requests.exceptions.RequestException as e:
            logger.error(f"Failed to connect to camera service: {e}")
            await self.send(text_data=json.dumps({'event': 'access_denied', 'reason': 'camera_service_error'}))
            await self.log_and_broadcast_access(user=user, device=device, is_success=False, failure_reason="camera_service_error")
        except Exception as e:
            logger.error(f"An unexpected error occurred during face verification: {e}", exc_info=True)
            await self.send(text_data=json.dumps({'event': 'access_denied', 'reason': 'internal_verification_error'}))
            await self.log_and_broadcast_access(user=user, device=device, is_success=False, failure_reason="internal_verification_error")

    async def handle_face_detection(self, data):
        """
        Handle face detection events from camera.
        """
        logger.info(f"Face detection event from device {self.device_id}")

        # Broadcast face detection event
        await self.channel_layer.group_send(
            self.device_group_name,
            {
                'type': 'face_detection_event',
                'device_id': self.device_id,
                'faces_detected': data.get('faces_detected', 0),
                'timestamp': data.get('timestamp')
            }
        )

    async def handle_door_status(self, data):
        """
        Handle door status updates.
        """
        status = data.get('status')  # 'open', 'closed', 'locked', 'unlocked'
        logger.info(f"Door status update from device {self.device_id}: {status}")

        # Broadcast door status
        await self.channel_layer.group_send(
            self.device_group_name,
            {
                'type': 'door_status_event',
                'device_id': self.device_id,
                'status': status,
                'timestamp': data.get('timestamp')
            }
        )

    async def handle_heartbeat(self, data):
        """
        Handle device heartbeat messages. Updates last_seen timestamp and marks
        an offline device as online again, broadcasting the status change.
        """
        # Process the heartbeat, which updates the last_seen time and potentially
        # changes the device status from 'offline' to 'online'.
        device, _ = await self.process_heartbeat_and_update_status()

        # Always broadcast the latest device status on every heartbeat to keep
        # the 'last_seen' time fresh on the admin UI.
        await self.channel_layer.group_send(
            'admin_monitor',
            {
                'type': 'device_status_update',
                'payload': await self.device_to_dict(device)
            }
        )

        # Respond to the device
        server_time = timezone.now()
        logger.debug(f"Heartbeat from {self.device_id}. Status: {device.status}. Server time: {server_time.isoformat()}")
        await self.send(text_data=json.dumps({
            'event': 'heartbeat_response',
            'timestamp': data.get('timestamp'),
            'server_time': server_time.isoformat()
        }))

    async def send_error(self, message):
        """
        Send error message to client.
        """
        await self.send(text_data=json.dumps({
            'event': 'error', # 统一协议为 event
            'message': message
        }))

    async def send_command(self, event):
        """
        Send command to device (called from group_send).
        """
        command = event['command']
        await self.send(text_data=json.dumps({
            'type': 'command',
            'command': command,
            'data': event.get('data', {})
        }))

    # Group event handlers
    async def nfc_scan_event(self, event):
        """
        Handle NFC scan events from group.
        """
        await self.send(text_data=json.dumps({
            'type': 'nfc_scan',
            'card_id': event['card_id'],
            'user_id': event['user_id'],
            'username': event['username'],
            'device_id': event['device_id'],
            'timestamp': event['timestamp']
        }))

    async def face_detection_event(self, event):
        """
        Handle face detection events from group.
        """
        await self.send(text_data=json.dumps({
            'type': 'face_detection',
            'device_id': event['device_id'],
            'faces_detected': event['faces_detected'],
            'timestamp': event['timestamp']
        }))

    async def door_status_event(self, event):
        """
        Handle door status events from group.
        """
        await self.send(text_data=json.dumps({
            'type': 'door_status',
            'device_id': event['device_id'],
            'status': event['status'],
            'timestamp': event['timestamp']
        }))

    @database_sync_to_async
    def process_heartbeat_and_update_status(self):
        """
        Updates the device's last_seen timestamp. If the device was marked as offline,
        it's updated to 'online'.
        Returns the device object and a boolean indicating if the status was changed.
        """
        device, created = Device.objects.get_or_create(
            device_id=self.device_id,
            defaults={'name': f"Device {self.device_id}", 'location': 'Unknown', 'status': 'online'}
        )
        
        status_changed = device.status == 'offline'
        
        update_fields = {'last_seen': timezone.now()}
        if status_changed or created:
            update_fields['status'] = 'online'
            update_fields['ip_address'] = self.scope['client'][0]
        
        Device.objects.filter(pk=device.pk).update(**update_fields)
        
        device.refresh_from_db()
        
        # Return true if the status was changed from offline OR the device was just created
        return device, status_changed or created

    @database_sync_to_async
    def authenticate(self):
        """
        Authenticate WebSocket connection using token or device key.
        Returns True if authentication successful, False otherwise.
        """
        query_string = self.scope.get('query_string', b'').decode()
        query_params = parse_qs(query_string)

        # Try token authentication first (for web clients)
        token = query_params.get('token', [None])[0]
        if token:
            try:
                token_obj = Token.objects.get(key=token)
                self.user = token_obj.user
                logger.info(f"User {self.user.username} authenticated via token")
                return True
            except ObjectDoesNotExist:
                logger.warning(f"Invalid token provided: {token}")

        # Try device key authentication (for ESP32 devices)
        device_key = query_params.get('device_key', [None])[0]
        if device_key:
            # In a real implementation, you'd validate the device key against a database
            # For now, we'll use a simple check
            expected_device_key = f"device_{self.device_id}_secret_key"
            if device_key == expected_device_key:
                self.user = AnonymousUser()
                self.is_device = True
                logger.info(f"Device {self.device_id} authenticated via device key")
                return True
            else:
                logger.warning(f"Invalid device key for device {self.device_id}")

        # No valid authentication found
        return False

    @database_sync_to_async
    def get_user_by_nfc_card(self, card_id):
        """
        Get user by NFC card ID.
        """
        try:
            from accounts.models import Profile
            profile = Profile.objects.get(nfc_card_id=card_id)
            return profile.user
        except ObjectDoesNotExist:
            return None

    @database_sync_to_async
    def get_device(self):
        device, _ = Device.objects.get_or_create(
            device_id=self.device_id,
            defaults={'name': f"Device {self.device_id}", 'location': 'Unknown'}
        )
        return device

    @database_sync_to_async
    def get_face_embedding_for_user(self, user: User) -> np.ndarray | None:
        """
        Retrieves the stored face embedding for a given user.
        """
        try:
            face_data = FaceData.objects.get(user=user)
            # Convert bytes back to numpy array
            embedding_bytes = face_data.embedding
            embedding_array = np.frombuffer(embedding_bytes, dtype=np.float32)
            return embedding_array
        except FaceData.DoesNotExist:
            return None

    async def log_and_broadcast_access(self, user, device, is_success, failure_reason="", snapshot_path=""):
        """
        Helper function to create an access log and broadcast it to both admins and the specific user.
        """
        log_entry = await self.create_access_log(
            user=user,
            device=device,
            is_success=is_success,
            snapshot_path=snapshot_path,
            failure_reason=failure_reason
        )

        log_payload = await self.log_to_dict(log_entry)

        # Broadcast to all admins
        await self.channel_layer.group_send(
            'admin_monitor',
            {
                'type': 'new_access_log',
                'payload': log_payload
            }
        )

        # If the log is associated with a specific user, send it to their personal channel
        if user:
            user_group_name = f'user_{user.id}'
            await self.channel_layer.group_send(
                user_group_name,
                {
                    'type': 'user_new_access_log',
                    'payload': log_payload
                }
            )

    @database_sync_to_async
    def update_device_status_in_db(self, status):
        device, created = Device.objects.get_or_create(
            device_id=self.device_id,
            defaults={'name': f"Device {self.device_id}", 'location': 'Unknown'}
        )
        device.status = status
        device.last_seen = timezone.now()
        if status == 'online':
            device.ip_address = self.scope['client'][0]
        device.save()
        return device

    async def update_and_broadcast_device_status(self, status):
        device = await self.update_device_status_in_db(status)
        await self.channel_layer.group_send(
            'admin_monitor',
            {
                'type': 'device_status_update',
                'payload': await self.device_to_dict(device)
            }
        )

    @database_sync_to_async
    def create_access_log(self, user, device, is_success, snapshot_path, failure_reason=""):
        return AccessLog.objects.create(
            user=user,
            device=device,
            is_success=is_success,
            snapshot_path=snapshot_path,
            failure_reason=failure_reason
        )

    @database_sync_to_async
    def device_to_dict(self, device):
        return {
            'id': device.pk,
            'device_id': device.device_id,
            'name': device.name,
            'location': device.location,
            'status': device.status,
            'ip_address': device.ip_address,
            'firmware_version': device.firmware_version,
            'last_seen': device.last_seen.isoformat() if device.last_seen else None,
            'created_at': device.created_at.isoformat(),
            'camera_service_name': device.camera_service_name,  # 添加摄像头服务名称字段
        }

    @database_sync_to_async
    def log_to_dict(self, log):
        user_avatar_url = None
        if log.user and hasattr(log.user, 'profile') and log.user.profile.avatar:
            user_avatar_url = log.user.profile.avatar.url
        
        return {
            'id': log.pk,
            'user': log.user.username if log.user else 'Unknown',
            'user_avatar': user_avatar_url,
            'device': log.device.name if log.device else 'Unknown Device',
            'device_id': log.device.device_id if log.device else 'N/A',
            'timestamp': log.timestamp.isoformat(),
            'is_success': log.is_success,
            'snapshot_path': log.snapshot_path,
        }



class AdminMonitorConsumer(AsyncWebsocketConsumer):
    """
    WebSocket consumer for admin monitoring dashboard.
    Only authenticated admin users can connect.
    """

    async def connect(self):
        """
        Handle admin monitor connection.
        """
        # Check if user is authenticated and is staff
        user = self.scope.get('user')
        if not user or user.is_anonymous or not user.is_staff:
            logger.warning("Unauthorized admin monitor connection attempt")
            await self.close(code=4003)  # Forbidden
            return

        self.user = user
        self.group_name = 'admin_monitor'

        # Join admin monitor group
        await self.channel_layer.group_add(
            self.group_name,
            self.channel_name
        )

        await self.accept()

        # Send connection established message
        await self.send(text_data=json.dumps({
            'type': 'connection_established',
            'message': 'Admin monitor connected successfully.'
        }))

        logger.info(f"Admin {self.user.username} connected to monitor. Sending initial state.")

        # Send the initial state (all devices and recent logs)
        await self.send_initial_device_list()
        await self.send_initial_log_list()

    async def disconnect(self, close_code):
        """
        Handle admin monitor disconnection.
        """
        # Only attempt to discard if the group_name was set (i.e., connect was successful)
        if hasattr(self, 'group_name'):
            await self.channel_layer.group_discard(
                self.group_name,
                self.channel_name
            )
        
        if hasattr(self, 'user') and self.user.is_authenticated:
            logger.info(f"Admin {self.user.username} disconnected from monitor. Code: {close_code}")
        else:
            logger.info(f"An unauthorized admin monitor disconnected. Code: {close_code}")

    async def receive(self, text_data):
        """
        Handles incoming messages. For now, we just log them, as most communication
        is one-way (server-to-client).
        """
        logger.debug(f"Received message from admin {self.user.username}: {text_data}")
        # Optionally, you could add command handling here in the future
        # For now, we do nothing with incoming messages.

    async def send_error(self, message):
        """
        Send error message to admin.
        """
        await self.send(text_data=json.dumps({
            'type': 'error',
            'message': message
        }))

    # --- Methods to send initial data ---

    async def send_initial_device_list(self):
        """
        Sends the full list of current devices to the newly connected client.
        """
        devices = await self.get_all_devices()
        await self.send(text_data=json.dumps({
            'type': 'initial_device_list',
            'payload': devices
        }))

    async def send_initial_log_list(self):
        """
        Sends the most recent access logs to the newly connected client.
        """
        logs = await self.get_recent_access_logs()
        await self.send(text_data=json.dumps({
            'type': 'initial_log_list',
            'payload': logs
        }))


    # --- Group event handlers (forwarding real-time updates) ---

    async def device_status_update(self, event):
        """
        Forward device status updates to the admin client.
        """
        await self.send(text_data=json.dumps({
            'type': 'device_status_update',
            'payload': event['payload']
        }))

    async def new_access_log(self, event):
        """
        Forward new access log entries to the admin client.
        """
        await self.send(text_data=json.dumps({
            'type': 'new_access_log',
            'payload': event['payload']
        }))

    # --- Database access methods ---

    @database_sync_to_async
    def get_all_devices(self):
        """
        Retrieves all devices from the database.
        """
        devices = Device.objects.all()
        # We need to await the async helper function here
        return [self.sync_device_to_dict(device) for device in devices]

    def sync_device_to_dict(self, device):
        """
        Synchronous version of device_to_dict for use within other sync functions.
        """
        return {
            'id': device.pk,
            'device_id': device.device_id,
            'name': device.name,
            'location': device.location,
            'status': device.status,
            'ip_address': device.ip_address,
            'firmware_version': device.firmware_version,
            'last_seen': device.last_seen.isoformat() if device.last_seen else None,
            'created_at': device.created_at.isoformat(),
            'camera_service_name': device.camera_service_name,  # 添加摄像头服务名称字段
        }

    @database_sync_to_async
    def get_recent_access_logs(self):
        """
        Get recent access logs from database, including device info.
        """
        logs = AccessLog.objects.select_related('user', 'device').order_by('-timestamp')[:50]
        return [self.sync_log_to_dict(log) for log in logs]


    def sync_log_to_dict(self, log):
        """
        Synchronous version of log_to_dict.
        """
        user_avatar_url = None
        if log.user and hasattr(log.user, 'profile') and log.user.profile.avatar:
            user_avatar_url = log.user.profile.avatar.url

        return {
            'id': log.pk,
            'user': log.user.username if log.user else 'Unknown',
            'user_avatar': user_avatar_url,
            'device': log.device.name if log.device else 'Unknown Device',
            'device_id': log.device.device_id if log.device else 'N/A',
            'timestamp': log.timestamp.isoformat(),
            'is_success': log.is_success,
            'snapshot_path': log.snapshot_path,
        }



class UserDashboardConsumer(AsyncWebsocketConsumer):
    """
    WebSocket consumer for user dashboard.
    Only authenticated users can connect and see their own data.
    """

    async def connect(self):
        """
        Handle user dashboard connection.
        """
        user = self.scope.get('user')
        if not user or user.is_anonymous:
            logger.warning("Unauthorized user dashboard connection attempt")
            await self.close(code=4003)  # Forbidden
            return

        self.user = user
        self.group_name = f'user_{user.id}'

        # Join user-specific group
        await self.channel_layer.group_add(
            self.group_name,
            self.channel_name
        )

        await self.accept()

        # Send initial user data
        await self.send(text_data=json.dumps({
            'type': 'connection_established',
            'user_id': self.user.id,
            'username': self.user.username,
            'message': 'User dashboard connected'
        }))

        logger.info(f"User {self.user.username} connected to dashboard")

    async def disconnect(self, close_code):
        """
        Handle user dashboard disconnection.
        """
        # Only attempt to discard if the group_name was set (i.e., connect was successful)
        if hasattr(self, 'group_name'):
            await self.channel_layer.group_discard(
                self.group_name,
                self.channel_name
            )

        if hasattr(self, 'user') and self.user.is_authenticated:
            logger.info(f"User {self.user.username} disconnected from dashboard. Code: {close_code}")
        else:
            logger.info(f"An unauthorized user disconnected from dashboard. Code: {close_code}")

    async def receive(self, text_data):
        """
        Handle user dashboard requests.
        """
        try:
            data = json.loads(text_data)
            command = data.get('command')

            if command == 'get_my_logs':
                await self.send_user_logs()
            elif command == 'get_my_profile':
                await self.send_user_profile()
            else:
                await self.send_error(f"Unknown command: {command}")

        except json.JSONDecodeError:
            await self.send_error("Invalid JSON format")

    async def send_user_logs(self):
        """
        Send user's access logs.
        """
        logs = await self.get_user_access_logs()
        await self.send(text_data=json.dumps({
            'type': 'user_logs',
            'logs': logs
        }))

    async def send_user_profile(self):
        """
        Send user profile information.
        """
        profile = await self.get_user_profile()
        await self.send(text_data=json.dumps({
            'type': 'user_profile',
            'profile': profile
        }))

    async def send_error(self, message):
        """
        Send error message to user.
        """
        await self.send(text_data=json.dumps({
            'type': 'error',
            'message': message
        }))

    # Group event handlers
    async def user_new_access_log(self, event):
        """
        Forward a new access log specific to this user.
        """
        await self.send(text_data=json.dumps({
            'type': 'new_access_log',
            'payload': event['payload']
        }))

    @database_sync_to_async
    def get_user_access_logs(self):
        """
        Get user's access logs from database.
        """
        try:
            from .models import AccessLog
            logs = AccessLog.objects.filter(user=self.user).order_by('-timestamp')[:10]
            return [
                {
                    'id': log.id,
                    'timestamp': log.timestamp.isoformat(),
                    'is_success': log.is_success,
                    'snapshot_path': log.snapshot_path
                }
                for log in logs
            ]
        except Exception as e:
            logger.error(f"Error fetching user access logs: {e}")
            return []

    @database_sync_to_async
    def get_user_profile(self):
        """
        Get user profile information.
        """
        try:
            profile = self.user.profile
            return {
                'username': self.user.username,
                'full_name': profile.full_name,
                'email': self.user.email,
                'nfc_card_id': profile.nfc_card_id,
                'avatar_url': profile.avatar.url if profile.avatar else None
            }
        except Exception as e:
            logger.error(f"Error fetching user profile: {e}")
            return {
                'username': self.user.username,
                'full_name': '',
                'email': self.user.email,
                'nfc_card_id': None,
                'avatar_url': None
            }